#include "os_inc.h"

/* hand_value.c:
 * compute value of a player hand
 *
 * (C) Copyright 2013 Ramone de Castillon 
 * ramone.castillon@gmail.com
 * http://poker-botting.blogspot.fr/.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 */

/* each combinaison of cards = 1 value
 * value between [PEV_FH, PEV_FH_MAX] are card value for full house.
 * the higest one is the best full house
 */
#define PEV_ERR 0xFFFFFFFF	/* error */
#define PEV_BASE (0xFFFFF+1)
#define PEV_H  0X0              /* high card */
#define PEV_P  PEV_BASE         /* pair */
#define PEV_DP (PEV_BASE * 2)   /* double pair */
#define PEV_B  (PEV_BASE * 3)   /* brelan */
#define PEV_S  (PEV_BASE * 4)	/* straight */
#define PEV_F  (PEV_BASE * 5)   /* flush */
#define PEV_FH (PEV_BASE * 6)   /* full house */
#define PEV_SQ (PEV_BASE * 7)	/* square */
#define PEV_QF (PEV_BASE * 8)	/* quinte flush */
#define PEV_R  (PEV_BASE * 9)	/* royal quinte flush */

/* some constant used for test purpose */
/* FIXME: fix value */
#define PEV_H_MAX  (PEV_H  + PEV_BASE - 1)
#define PEV_P_MAX  (PEV_P  + PEV_BASE - 1)
#define PEV_DP_MAX (PEV_DP + PEV_BASE - 1)
#define PEV_B_MAX  (PEV_B  + PEV_BASE - 1)
#define PEV_S_MAX  (PEV_S  + PEV_BASE - 1)
#define PEV_F_MAX  (PEV_F  + PEV_BASE - 1)
#define PEV_FH_MAX (PEV_FH + PEV_BASE - 1)
#define PEV_SQ_MAX (PEV_SQ + PEV_BASE - 1)
#define PEV_QF_MAX (PEV_QF + PEV_BASE - 1)
#define PEV_R_MAX  (PEV_R)

enum e_card_value {
	PE_HV_NONE = 0,
	PE_HV_HIGH,
	PE_HV_PAIR,
	PE_HV_DPAIR,
	PE_HV_BRELAN,
	PE_HV_STRAIGHT,
	PE_HV_FLUSH,
	PE_HV_FULLHOUSE,
	PE_HV_SQUARE,
	PE_HV_QUINTEFLUSH,
	PE_HV_ROYALQUINTEFLUSH,
	PE_HV_END
};

struct scard {
	uint8_t value;
	uint8_t color;
	uint16_t priv;	/* can be used for private calculation */
	struct scard *next;
};

static uint32_t __reset_scard_private(struct scard *card)
{
	do {
		card->priv = 0;
	}while((card = card->next));
	return 0;
}

static uint32_t __compute_hv_high(struct scard *card, int nbCards, int weight)
{
	uint32_t res = 0;
	
	do {
		if (card->priv == 0) {
			res+=card->value*weight;
			card->priv = 1;
			weight/=13;
			nbCards--;
		}
	} while((card = card->next) && nbCards);

	if (nbCards)
		return PEV_ERR;
	assert(res <= PEV_H_MAX);
	return res;
}

/* compute value if player has heigh card (ie: not pair, brelan, full, etc ...)*/
static uint32_t compute_hv_high(struct scard *card)
{
	uint32_t res = __compute_hv_high(card, 5, 1*13*13*13*13);
	if (res == PEV_ERR) return res;
	res+=PEV_H;
	return res;
}

static uint32_t __compute_hv_pair(struct scard *card, int nbPair, int weight)
{
	uint32_t res = 0;
	struct scard *card2 = card->next;
	
	do {
		if ((card->value == card2->value) && 
		    (card->priv == 0 && card2->priv == 0)) {
			card->priv = 1;
			card2->priv = 1;
			res+=card->value*weight;
			weight/=13;
			nbPair--;
		}
	} while((card = card->next) && (card2 = card2->next) && nbPair);

	if (nbPair)
		return PEV_ERR;
	return res;
}

static uint32_t compute_hv_pair(struct scard *card)
{
	uint32_t res = 0, tmp;
	
	__reset_scard_private(card);

	/* find the pair */
	res = __compute_hv_pair(card, 1, 1*13*13*13);
	if (res == PEV_ERR)
		return res;

	/* 3 remaining cards */
	tmp = __compute_hv_high(card, 3, 1*13*13);
	if (tmp == PEV_ERR) return tmp;
	res+=tmp;

	/* pair value start from PEV_P */
	res += PEV_P;
	assert(res <= PEV_P_MAX);
	return res;
}

static uint32_t compute_hv_dpair(struct scard *card)
{
	uint32_t res = 0, tmp;
	
	__reset_scard_private(card);

	/* find 2 pairs */
	res = __compute_hv_pair(card, 2, 1*13*13);
	if (res == PEV_ERR)
		return res;

	/* remaining card */
	tmp = __compute_hv_high(card, 1, 1);
	if (tmp == PEV_ERR) return tmp;
	res+=tmp;

	/* double pair value start from PEV_DP */
	res += PEV_DP;
	assert(res <= PEV_DP_MAX);
	return res;
}

static uint32_t __compute_hv_brelan(struct scard *card, int weight)
{
	struct scard *card2 = card->next;
	struct scard *card3 = card2->next;

	do {
		if (card->value == card2->value && card->value == card3->value &&
		    card->priv == 0 && card2->priv == 0 && card3->priv == 0) {
			card->priv  = 1;
			card2->priv = 1;
			card3->priv = 1;
			return card->value*weight;
		}
	} while((card = card->next) && (card2 = card2->next) && (card3 = card3->next));
	return PEV_ERR;
}

static uint32_t compute_hv_brelan(struct scard *card)
{
	uint32_t tmp;
	uint32_t res;

	__reset_scard_private(card);
	res = __compute_hv_brelan(card, 1*13*13);
	if (res == PEV_ERR) return PEV_ERR;

	/* 2 remaining cards */
	tmp = __compute_hv_high(card, 2, 1*13);
	if (tmp == PEV_ERR) return tmp;
	res+=tmp;

	/* brelan value start from PEV_DP */
	res += PEV_B;
	assert(res <= PEV_B_MAX);
	return res;
}

/* ignore cards with card->priv = 1 (usefull for quinte flush) */
static uint32_t __compute_hv_straight_As_low(struct scard *card)
{
	static uint32_t straight_low_As_value = 0*13*13*13*13+1*13*13*13+2*13*13+3*13+4;
	int straight[5]={0,};
	int i;

	do {
		/* priv = 1 => we are looking for quinte flush
		   and this card doesn't have the needed color */
		if (card->priv == 1)
			continue;
		else if (card->value <=3) {
			straight[card->value]=1;
		}
		else if (card->value == 12)
			straight[4]=1;
	}while((card=card->next));

	int nbCards=0;
	for(i=0;i<5;i++)
		if (straight[i]) nbCards++;
	if (nbCards == 5)
		return straight_low_As_value;
	else
		return PEV_ERR;	
}

/* ignore cards with card->priv = 1 (usefull for quinte flush) */
static uint32_t __compute_hv_straight(struct scard *card)
{
	int nbCards=0;
	uint32_t res = 0;
	int weight=1*13*13*13*13;
	struct scard *ncard = card->next;
	struct scard *fcard = card;

	do {
		/* priv = 1 => we are looking for quinte flush
		   and this card doesn't have the needed color */
		if (card->priv == 1) {
			card=card->next;
			ncard=card->next;
			continue;
		}
		else if(ncard->priv == 1) {
			ncard=ncard->next;
			continue;
		}
		/* same card value, cannot be counted  */
		else if (card->value == ncard->value) {
			card=card->next;
			ncard=card->next;
			continue;
		}
		/* 1 more card to be counted as straight combination */
		else if (card->value == ncard->value+1) {
			if (nbCards == 0) {
				res+=card->value*weight;
				nbCards++;
				weight/=13;
			}
			nbCards++;
			res+=ncard->value*weight;
			weight/=13;
		}
		/* cards are ordered */
		else {
			res=0;
			nbCards=0;
			weight = 1*13*13*13*13;
		}
		card=card->next;
		ncard=card->next;
		
	}while(nbCards < 5 && card && ncard);

	if (nbCards != 5) return __compute_hv_straight_As_low(fcard);	
	assert(res && weight == 0);
	/* add +1 because +0 is reserved for special case 1,2,3,4,5*/
	return res+1;
}

static uint32_t compute_hv_straight(struct scard *card)
{
	uint32_t res;
	__reset_scard_private(card);

	res = __compute_hv_straight(card);
	if (res == PEV_ERR) return PEV_ERR;

	/* straight value start from PEV_S */
	res += PEV_S;
	assert(res <= PEV_S_MAX);
	return res;
}

static uint32_t __compute_hv_flush(struct scard *card)
{
	struct scard *_card;
	uint8_t colors[4];
	uint8_t color;
	memset(colors, 0, sizeof(colors));
	
	/* count color */
	_card = card;
	do {
		colors[_card->color]++;
	}while((_card = _card->next));

	/* do we have 5 cards wuth the same color ? */
	for(color=0;color<4;color++) {
		if (colors[color] >= 5)
			break;
	}
	if (color >= 4) return 1;

	/* mark not flush card */
	//_card = card;
	do {
		if (card->color != color)
			card->priv = 1;
	}while((card = card->next));

	return 0;
}

static uint32_t compute_hv_flush(struct scard *card)
{
	int weight;
	int res;
	
	__reset_scard_private(card);

	/* do we have flush cards ? */
	res = __compute_hv_flush(card);
	if (res != 0) return PEV_ERR;

	/* compute value */
	weight=1*13*13*13*13;
	res=0;
	do {
		if (card->priv == 0) {
			res+=card->value * weight;		
			weight/=13;
		}
	}while((card = card->next));
	assert(res && weight == 0);

	/* Flush value start from PEV_F */
	res += PEV_F;
	assert(res <= PEV_F_MAX);
	return res;
}

static uint32_t compute_hv_fullHouse(struct scard *card)
{
	uint32_t tmp;
	uint32_t res = __compute_hv_brelan(card, 1*13);
	if (res == PEV_ERR) return res;

	/* remaining pair */
	tmp = __compute_hv_pair(card, 1, 1);
	if (tmp == PEV_ERR) return tmp;
	res+=tmp;

	/* full house value start from PEV_FH */
	res += PEV_FH;
	assert(res <= PEV_FH_MAX);
	return res;
}

static uint32_t __compute_hv_square(struct scard *card, int weight)
{
	struct scard *card2 = card->next;
	struct scard *card3 = card2->next;
	struct scard *card4 = card3->next;

	do {
		if (card->value == card2->value && card->value == card3->value &&
		    card->value == card4->value && card->priv == 0 &&
		    card2->priv == 0 && card3->priv == 0 && card4->priv == 0) {
			card->priv  = 1;
			card2->priv = 1;
			card3->priv = 1;
			card4->priv = 1;
			return card->value*weight;
		}
	} while((card = card->next) && (card2 = card2->next) && 
		(card3 = card3->next) && (card4 = card4->next));
	return PEV_ERR;
}


static uint32_t compute_hv_square(struct scard *card)
{
	uint32_t tmp;
	uint32_t res = __compute_hv_square(card, 1*13);
	if (res == PEV_ERR) return res;

	/* remaining card */
	tmp = __compute_hv_high(card, 1, 1);
	if (tmp == PEV_ERR) return tmp;
	res+=tmp;

	/* square value start from PEV_SQ */
	res += PEV_SQ;
	assert(res <= PEV_SQ_MAX);
	return res;
}

static uint32_t compute_hv_quinteflush(struct scard *card)
{
	static uint32_t quinteflushroyal_value = 12*13*13*13*13+11*13*13*13+10*13*13+9*13+8;
	uint32_t res;

	__reset_scard_private(card);

	/* do we have flush cards ? */
	res = __compute_hv_flush(card);
	if (res != 0) return PEV_ERR;

	res = __compute_hv_straight(card);
	if (res == PEV_ERR) return res;

	/* quinte flush royal ? */
	if (res == quinteflushroyal_value)
		return PEV_R;

	/* quinte flush value start from PEV_QF */
	res += PEV_QF;
	assert(res <= PEV_QF_MAX);
	return res;
}

static uint32_t compute_hv_royalquinteflush(struct scard *UNUSED(card))
{
	/*computed by computeCVQuinteFlush */
	return PEV_ERR;
}
	
struct card_value {
	uint32_t base;
	uint32_t max;
	uint32_t (*valueFct)(struct scard *card);
}cardsValue[PE_HV_END] = {
	[PE_HV_HIGH] = {
		.base=PEV_H,
		.max=PEV_H_MAX,
		.valueFct=compute_hv_high
	},
	[PE_HV_PAIR] = {
		.base=PEV_P,
		.max=PEV_P_MAX,
		.valueFct=compute_hv_pair
	},
	[PE_HV_DPAIR] = {
		.base=PEV_DP,
		.max =PEV_DP_MAX,
		.valueFct=compute_hv_dpair
	},
	[PE_HV_BRELAN] = {
		.base=PEV_B,
		.max =PEV_B_MAX,
		.valueFct=compute_hv_brelan
	},
	[PE_HV_STRAIGHT] = {
		.base=PEV_S,
		.max =PEV_S_MAX,
		.valueFct=compute_hv_straight
	},
	[PE_HV_FLUSH] = {
		.base=PEV_F,
		.max =PEV_F_MAX,
		.valueFct=compute_hv_flush
	},
	[PE_HV_FULLHOUSE] = {
		.base=PEV_FH,
		.max =PEV_FH_MAX,
		.valueFct=compute_hv_fullHouse
	},
	[PE_HV_SQUARE] = {
		.base=PEV_SQ,
		.max =PEV_SQ_MAX,
		.valueFct=compute_hv_square
	},
	[PE_HV_QUINTEFLUSH] = {
		.base=PEV_QF,
		.max =PEV_QF_MAX,
		.valueFct=compute_hv_quinteflush
	},
	[PE_HV_ROYALQUINTEFLUSH] = {
		.base=PEV_R,
		.max =PEV_R_MAX,
		.valueFct=compute_hv_royalquinteflush
	},
};

static uint32_t __compute_value(struct scard *card)
{
	uint32_t value = PEV_ERR;
	int i;

	for(i=PE_HV_ROYALQUINTEFLUSH;i>PE_HV_NONE;i--) {
		value = cardsValue[i].valueFct(card);
		if (value != PEV_ERR) {
			return value;
		}
	}

	assert(value != PEV_ERR);
	return value;
}

static void cardcode_2_scard(uint8_t code, struct scard *c)
{
	if (!os_is_bad_card(code)) {
		c->value = code%13;
		c->color = code/13;
	}
	else {
		c->value = code;
		c->color = code;
	}
	c->priv = 0;
}

/* order board card from highest to lowest */
static void os_begin_order_cards(uint8_t *ocards)
{
	int i,j;

	/* order board card from highest to lowest */
	for(i=0;i<5;i++) {
		if (ocards[i] == OS_CARD_NONE) break;
		for(j=i+1;j<5;j++) {
			if (ocards[j] == OS_CARD_NONE) break;
			if (ocards[i]%13 < ocards[j]%13)
				OS_SWAP(ocards[i],ocards[j]);	
		}
	}
	assert(ocards[0]%13 >= ocards[1]%13);
	assert(ocards[1]%13 >= ocards[2]%13);
	if (ocards[3] != OS_CARD_NONE) {
		assert(ocards[2]%13 >= ocards[3]%13);
		if (ocards[4] != OS_CARD_NONE)
			assert(ocards[3]%13 >= ocards[4]%13);
	}
}

/* order all cards (player's cards and board's cards) from highest to lowest value */
static void __order_end_scard_by_value(struct scard **head, struct scard *ci)
{
	struct scard *c = *head;

	if (ci->value >= (*head)->value) {
		ci->next = *head;
		*head = ci;
		return;
	}

	while(c->next) {
		if (ci->value > c->next->value) {
			ci->next = c->next;
			c->next = ci;
			return;
		}
		c = c->next;
	};
	ci->next = NULL;
	c->next = ci;
}

/* all card are already ordered except the 2 1st one */
static void order_end_scard_by_value(struct scard **head)
{
	struct scard *c1;
	struct scard *c2;
	
	/* cards to order */
	c1 = *head;
	c2 = c1->next;

	*head = c2->next;
	__order_end_scard_by_value(head, c1);
	__order_end_scard_by_value(head, c2);
}

uint32_t os_compute_hand_value(uint8_t pcards[2], uint8_t ocards[5])
{
	uint32_t value = PEV_ERR;
	struct scard card[7];
	struct scard *orderedCards;
	int i;

	/* don't know player's card */
	if (os_is_bad_card(pcards[0]) || os_is_bad_card(pcards[1])) {
		fprintf(stderr, "bad player cards\n");
		assert(0);
		return value;
	}

	os_begin_order_cards(ocards);

	/* cannot do anything untill going to flop */
/*	if (g.state <= PE_GS_PREFLOP)
		return value;
*/
	/* the game was finished while we still don't saw the cards */
/*	if (peIsBadCard(gcards[0]))
		return value;
*/
	/* get all cards */
	memset(card, OS_CARD_NONE, sizeof(card));
	for(i=0;i<6;i++)
		card[i].next= & card[i+1];
	card[6].next = NULL;
	cardcode_2_scard(pcards[0], &card[0]);	
	cardcode_2_scard(pcards[1], &card[1]);
	for(i=0;i<5;i++) {
		if (ocards[i] == OS_CARD_NONE)
			break;
		cardcode_2_scard(ocards[i], &card[2+i]);
	}
#if 0
	//if (g.state >= PE_GS_TURN) {
		cardCodeToScard(ocards[3], &card[5]);
		card[4].next = & card[5];
		card[5].next = NULL;
	//}
	//if (g.state >= PE_GS_RIVER) {
		cardCodeToScard(ocards[4], &card[6]);
		card[5].next = & card[6];
		card[6].next = NULL;
	//}
#endif
	//}
	assert(i>=3 && i<=5);
	card[1+i].next = NULL;

	/* order them from high value to low value */
	orderedCards = card;
	order_end_scard_by_value(&orderedCards);

	return __compute_value(orderedCards);
}

uint32_t os_card_2_code(uint8_t card[2])
{
	int code = 0;
	int c1 = card[0]%13; 
	int c2 = card[1]%13; 
	if (c1>c2) OS_SWAP(c1,c2);

	code = (c1 + c2*13)*2;
	
	/* suited cards */
	if (card[0]/13 == card[1]/13)
		code++;

	assert(code <338);

	return code;

}

const char *os_hand_value_2_text(uint32_t value)
{
	if (value == PEV_ERR)
		return "Error";
	else if (value == PEV_R)
		return "Royal quinte flush";
	else if (value >= PEV_QF)
		return "Quinte flush";
	else if (value >= PEV_SQ)
		return "Square";
	else if (value >= PEV_FH)
		return "Full House";
	else if (value >= PEV_F)
		return "Flush";
	else if (value >= PEV_S)
		return "Straight";
	else if (value >= PEV_B)
		return "Brelan";
	else if (value >= PEV_DP)
		return "Dble pair";
	else if (value >= PEV_P)
		return "Pair";
	else /*if (value >= PEV_H)*/
		return "High";
}

